#!/usr/bin/env python
#
# Copyright 2009 Denis Mingulov
#
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

from google.appengine.ext import db
from google.appengine.ext.db import polymodel

from google.appengine.api import memcache

import persons

def buyModule(pers, moduletype):
    if pers is None:
        return
    if not isinstance(moduletype, ModuleType):
        return

    module = moduletype.createModule()
    module.person = pers
    module.resetAttrToType(moduletype)
    module.put()
    return module

def getModuleTypeSpecialAbilities(moduletype, forceReload=False):
    hashString = "specAbil_moduletype_'%s'" % moduletype.key()
    if not forceReload:   
        data = memcache.get( hashString )
        if data is not None:
            return data

    query = SpecialAbility.gql("WHERE type=:type AND status>0", type = moduletype.key())
    specabilities = query.fetch(1000)
    
    if specabilities is not None:
        memcache.set(hashString, specabilities)

    return specabilities

def getModuleTypeByNameWeight(moduleType, moduleName, moduleWeight):
    query = moduleType.gql("WHERE name=:name AND weight=:weight", name=moduleName, weight=moduleWeight)
    module = query.get()
    if module is None:
        print moduleWeight
        print moduleType
        print unicode(moduleName)
    return module

def addSpecialAbility(moduletype, priority, name, value, relative):
    if moduletype is None:
       print "Error specabil %s" % (moduletype)
       return None
    if not isinstance(moduletype, ModuleType):
       print "Error specabil %s" % (moduletype.name)
       return None
    specabil = SpecialAbility(type=moduletype, priority=priority, name=name, value=value, relative=relative)
    specabil.put()
    return specabil

class ModuleType(polymodel.PolyModel):
    name = db.StringProperty(required=True, multiline=False, indexed=True)
    description = db.TextProperty()
    cellcount = db.IntegerProperty(default=1)
    weight = db.IntegerProperty(default=0)
    #intlevel = db.IntegerProperty(default=0)
    status = db.IntegerProperty(default=1, indexed=True)
    pictureid = db.StringProperty(multiline=False, indexed=True)

    hasAttrName = True
    hasAttrDescription = True
    hasAttrCellcount = True
    hasAttrWeight = True
    hasAttrStatus = True
    hasAttrPictureId = True
    
    def createModule(self):
        module = Module(type = self, name = self.name)
        return module
            
    def __str__(self):
        return "%s '%s' status %d picid '%s' cellcount %d weight %d" % (self.__class__.__name__, self.name, self.status, self.pictureid, self.cellcount, self.weight)
        #return "%s: cellcount %d weight %d" % (self.__class__.__name__, self.cellcount, self.weight)

#    def updateSpecialAbilities

class AwardType(ModuleType):
    def createModule(self):
        module = Award(type = self, name = self.name)
        return module
            
    pass

class BattleRankType(ModuleType):
    def createModule(self):
        module = BattleRank(type = self, name = self.name)
        return module
            
    pass

class MaterialType(ModuleType):
    def createModule(self):
        module = Material(type = self, name = self.name)
        return module

    pass
  
class RocketType(MaterialType):
    def createModule(self):
        module = Rocket(type = self, name = self.name)
        return module

    pass

class DestroyableModuleType(ModuleType):
    maxdeterioration = db.IntegerProperty(default=2)
    hasAttrMaxDeterioration = True
    def createModule(self):
        module = DestroyableModule(type = self, name = self.name)
        return module
    def __str__(self):
        return super(DestroyableModuleType, self).__str__() + " max.det %d" % self.maxdeterioration

class EnergyConsumerType(DestroyableModuleType):
    energy = db.IntegerProperty(default=0)
    hasAttrEnergy = True
    def createModule(self):
        module = EnergyConsumer(type = self, name = self.name)
        return module
    def __str__(self):
        return super(EnergyConsumerType, self).__str__() + " energy %d" % self.energy


class ShipCaseType(EnergyConsumerType):
    layout = db.StringProperty(multiline=False)
    canBeRobbered = db.BooleanProperty(default=True)
    hyperengine = db.IntegerProperty(default=0)
    navcomp = db.IntegerProperty(default=0)
    navradar = db.IntegerProperty(default=0)
    hitreduce = db.IntegerProperty(default=0)

    hasAttrCanBeRobbered = True
    hasAttrLayout = True
    hasAttrHyperEngine = True
    hasAttrNavComp = True
    hasAttrNavRadar = True
    hasAttrHitReduce = True

    def createModule(self):
        module = ShipCase(type = self, name = self.name)
        return module
    def __str__(self):
        return super(ShipCaseType, self).__str__() + " rob %d" % self.canBeRobbered

# usual ship modules (might give bonuses etc)
class ConcreteType(EnergyConsumerType):
    def createModule(self):
        module = Concrete(type = self, name = self.name)
        return module

    def __str__(self):
        return super(ConcreteType, self).__str__()
  
class TechnologyType(ConcreteType):
    def createModule(self):
        module = Technology(type = self, name = self.name)
        return module

    pass

class ComputerType(ConcreteType):
    speed = db.IntegerProperty(default=0)
    hasAttrSpeed = True
    def createModule(self):
        module = Computer(type = self, name = self.name)
        return module
    def __str__(self):
        return super(ComputerType, self).__str__() + " speed %d" % self.speed

class BattleComputerType(ComputerType):
    def createModule(self):
        module = BattleComputer(type = self, name = self.name)
        return module
    pass

class NavigationComputerType(ComputerType):
    def createModule(self):
        module = NavigationComputer(type = self, name = self.name)
        return module
    pass

class CargoType(ConcreteType):
    capacity = db.IntegerProperty(default=0)
    hasAttrCapacity = True
    def createModule(self):
        module = Cargo(type = self, name = self.name)
        return module

    def __str__(self):
        return super(CargoType, self).__str__() + " cap %d" % self.capacity

class ContainerType(CargoType):
    def createModule(self):
        module = Container(type = self, name = self.name)
        return module

    pass

class HangarType(CargoType):
    def createModule(self):
        module = Hangar(type = self, name = self.name)
        return module

    pass

class FuelTankType(CargoType):
    def createModule(self):
        module = FuelTank(type = self, name = self.name)
        return module

    pass

class HoldType(CargoType):
    def createModule(self):
        module = Hold(type = self, name = self.name)
        return module

    pass

class EngineType(ConcreteType):
    speed = db.IntegerProperty(default=0)
    hasAttrSpeed = True
    def createModule(self):
        module = Engine(type = self, name = self.name)
        return module

    def __str__(self):
        return super(EngineType, self).__str__() + " speed %d" % self.speed

class HyperEngineType(EngineType):
    def createModule(self):
        module = HyperEngine(type = self, name = self.name)
        return module

    pass

class PlanetEngineType(EngineType):
    def createModule(self):
        module = PlanetEngine(type = self, name = self.name)
        return module

    pass

class PowerBoardType(ConcreteType):
    shield = db.IntegerProperty(default=0)
    hasAttrShield = True
    def createModule(self):
        module = PowerBoard(type = self, name = self.name)
        return module

    def __str__(self):
        return super(PowerBoardType, self).__str__() + " shield %d" % self.shield

class PowerUnitType(ConcreteType):
    power = db.IntegerProperty(default=0)
    hasAttrPower = True
    def createModule(self):
        module = PowerUnit(type = self, name = self.name)
        return module

    def __str__(self):
        return super(PowerUnitType, self).__str__() + " power %d" % self.power

class ExpandType(ConcreteType):
    def createModule(self):
        module = Expand(type = self, name = self.name)
        return module

    pass

class CompartmentType(ConcreteType):
    protection = db.IntegerProperty(default=0)
    hasAttrProtection = True
    def createModule(self):
        module = Compartment(type = self, name = self.name)
        return module

    def __str__(self):
        return super(CompartmentType, self).__str__() + " prot %d" % self.protection

class RadarType(ConcreteType):
    sensitivity = db.IntegerProperty(default=0)
    hasAttrSensitivity = True
    def createModule(self):
        module = Radar(type = self, name = self.name)
        return module

    def __str__(self):
        return super(RadarType, self).__str__() + " sens %d" % self.sensitivity

class LongRangeRadarType(RadarType):
    def createModule(self):
        module = LongRangeRadar(type = self, name = self.name)
        return module

    pass

class NavigationRadarType(RadarType):
    def createModule(self):
        module = NavigationRadar(type = self, name = self.name)
        return module

    pass

class BattleRadarType(RadarType):
    def createModule(self):
        module = BattleRadar(type = self, name = self.name)
        return module

    pass

class JammerRadarType(RadarType):
    def createModule(self):
        module = JammerRadar(type = self, name = self.name)
        return module

    pass

class WeaponType(ConcreteType):
    distance = db.IntegerProperty(default=0)
    accuracy = db.IntegerProperty(default=0)
    od = db.IntegerProperty(default=0)
    rateoffire = db.IntegerProperty(default=0)
    minshot = db.IntegerProperty(default=0)
    maxshot = db.IntegerProperty(default=0)
    radius = db.IntegerProperty(default=0)
    recharge = db.IntegerProperty(default=0)
    hasAttrDistance = True
    hasAttrAccuracy = True
    hasAttrOd = True
    hasAttrRateOfFire = True
    hasAttrMinShot = True
    hasAttrMaxShot = True
    hasAttrRadius = True
    hasAttrRecharge = True
    def createModule(self):
        module = Weapon(type = self, name = self.name)
        return module

    def __str__(self):
        return super(WeaponType, self).__str__() + " rof %d dist %d od %d acc %d radius %d recharge %d minshot %d maxshot %d" % (self.distance, self.rateoffire, self.od, self.accuracy, self.radius, self.recharge, self.minshot, self.maxshot)

class RechargableWeaponType(WeaponType):
    maxcharge = db.IntegerProperty(default=0)
    rockettype = db.ReferenceProperty(RocketType, required=True, indexed=True)
    hasAttrMaxCharge = True
    def createModule(self):
        module = RechargableWeapon(type = self, name = self.name)
        return module

    def __str__(self):
        return super(RechargableWeaponType, self).__str__() + " maxcharge %d" % self.maxcharge

class RocketWeaponType(RechargableWeaponType):
    def createModule(self):
        module = RocketWeapon(type = self, name = self.name)
        return module

    pass

class MineWeaponType(RechargableWeaponType):
    timecount = db.IntegerProperty(default=0)
    hasAttrTimeCount = True
    def createModule(self):
        module = MineWeapon(type = self, name = self.name)
        return module

    def __str__(self):
        return super(MineWeaponType, self).__str__() + " timecount %d" % self.timecount

class GravityWeaponType(RechargableWeaponType):
    def createModule(self):
        module = GravityWeapon(type = self, name = self.name)
        return module

    pass

class LaserWeaponType(WeaponType):
    def createModule(self):
        module = LaserWeapon(type = self, name = self.name)
        return module

    pass

class PlasmaWeaponType(WeaponType):
    def createModule(self):
        module = PlasmaWeapon(type = self, name = self.name)
        return module

    pass

class EnergyWeaponType(WeaponType):
    def createModule(self):
        module = EnergyWeapon(type = self, name = self.name)
        return module

    pass

#class WeaponRocketLink(polymodel.PolyModel):
#    weapon = db.ReferenceProperty(RechargableWeaponType, indexed=True)
#    rocket = db.ReferenceProperty(RocketType, indexed=True)
    
class ModuleTypeGroup(polymodel.PolyModel):
    name = db.StringProperty(required=True, multiline=False, indexed=True)
    type = db.StringProperty(required=True, multiline=False, indexed=True)
    general = db.SelfReferenceProperty(indexed=True)
  

# ----------------------------------------------------------------------------------
class SpecialAbility(polymodel.PolyModel):
    type = db.ReferenceProperty(ModuleType, required=True, indexed=True)
    priority = db.IntegerProperty(default=0, indexed=True)
    status = db.IntegerProperty(default=1, indexed=True)
    name = db.StringProperty(required=True, multiline=False, indexed=True)
    relative = db.BooleanProperty(default=False, indexed=True)
    value = db.IntegerProperty(default=0, required=True, indexed=True)

# ----------------------------------------------------------------------------------
class Module(polymodel.PolyModel):
    person = db.ReferenceProperty(persons.Person, indexed=True)
    type = db.ReferenceProperty(ModuleType, required=True, indexed=True)
    dateCreation =  db.DateTimeProperty(auto_now_add=True)
    dateChange =  db.DateTimeProperty(auto_now=True)
    name = db.StringProperty(required=True, multiline=False, indexed=True)
    
    cellcount = db.IntegerProperty(default=1)
    weight = db.IntegerProperty(default=0)
    status = db.IntegerProperty(default=1, indexed=True)
   
    def resetAttrToType(self, moduletype):
        self.cellcount = moduletype.cellcount
        self.weight = moduletype.weight

    def addToFighter(self, fighter):
        fighter.shipWeight += self.weight

    hasAttrName = True
    hasAttrCellcount = True
    hasAttrWeight = True
    hasAttrStatus = True
    
class Award(Module):
    def resetAttrToType(self, moduletype):
        super(Award, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        super(Award, self).addToFighter(fighter)

    pass

class BattleRank(Module):
    def resetAttrToType(self, moduletype):
        super(BattleRank, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        super(BattleRank, self).addToFighter(fighter)

    pass


class DestroyableModule(Module):
    maxdeterioration = db.IntegerProperty(default=2)
    deterioration = db.IntegerProperty(default=2)

    def resetAttrToType(self, moduletype):
        self.maxdeterioration = moduletype.maxdeterioration
        self.deterioration = moduletype.maxdeterioration
        super(DestroyableModule, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        super(DestroyableModule, self).addToFighter(fighter)

    hasAttrMaxDeterioration = True
    hasAttrDeterioration = True

class EnergyConsumer(DestroyableModule):
    energy = db.IntegerProperty(default=0)
    hasAttrEnergy = True
    def resetAttrToType(self, moduletype):
        self.energy = moduletype.energy
        super(EnergyConsumer, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        fighter.shipEnergy += self.energy
        super(EnergyConsumer, self).addToFighter(fighter)

class ShipCase(EnergyConsumer):
    layout = db.StringProperty(multiline=False)
    canBeRobbered = db.BooleanProperty(default=True)
    hyperengine = db.IntegerProperty(default=0)
    navcomp    = db.IntegerProperty(default=0)
    navradar   = db.IntegerProperty(default=0)
    hitreduce  = db.IntegerProperty(default=0)

    def resetAttrToType(self, moduletype):
        self.layout = moduletype.layout
        self.canBeRobbered = moduletype.canBeRobbered
        self.hyperengine = moduletype.hyperengine
        self.navcomp     = moduletype.navcomp    
        self.navradar    = moduletype.navradar   
        self.hitreduce   = moduletype.hitreduce  
        self.energy      = moduletype.energy
        super(ShipCase, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        fighter.shipTotalCells += self.cellcount
        fighter.shipNavCompSpeed += self.navcomp
        fighter.shipNavRadarSensitivity += self.navradar
        fighter.shipHitReduce += self.hitreduce
        fighter.shipHyperEngineSpeed += self.hyperengine
        super(ShipCase, self).addToFighter(fighter)

    hasAttrCanBeRobbered = True
    hasAttrLayout = True
    hasAttrHyperEngine = True
    hasAttrNavComp = True
    hasAttrNavRadar = True
    hasAttrHitReduce = True

# usual ship modules (might give bonuses etc)
class Concrete(EnergyConsumer):
    def resetAttrToType(self, moduletype):
        super(Concrete, self).resetAttrToType(moduletype)

    def addToFighter(self, fighter):
        fighter.shipUsedCells += self.cellcount
        super(Concrete, self).addToFighter(fighter)

  
class Computer(Concrete):
    speed = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.speed = moduletype.speed
        super(Computer, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter):
        super(Computer, self).addToFighter(fighter)
    hasAttrSpeed = True

class BattleComputer(Computer):
    def addToFighter(self, fighter): 
        fighter.shipBattleCompSpeed += self.speed
        super(BattleComputer, self).addToFighter(fighter)
    pass

class NavigationComputer(Computer):
    def addToFighter(self, fighter): 
# disabled
#        fighter.shipNavCompSpeed += self.speed
        super(NavigationComputer, self).addToFighter(fighter)
    pass

class Cargo(Concrete):
    capacity = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.capacity = moduletype.capacity
        super(Cargo, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        super(Cargo, self).addToFighter(fighter)
    hasAttrCapacity = True

class Container(Cargo):
    def addToFighter(self, fighter): 
        fighter.shipContainerCapacity += self.capacity
        super(Container, self).addToFighter(fighter)
    pass

class Hangar(Cargo):
    def addToFighter(self, fighter): 
        fighter.shipHangarCapacity += self.capacity
        super(Hangar, self).addToFighter(fighter)
    pass

class FuelTank(Cargo):
    def addToFighter(self, fighter): 
        fighter.shipFuelTankCapacity += self.capacity
        super(FuelTank, self).addToFighter(fighter)
    pass

class Hold(Cargo):
    def addToFighter(self, fighter): 
        fighter.shipHoldCapacity += self.capacity
        super(Hold, self).addToFighter(fighter)
    pass

class Engine(Concrete):
    speed = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.speed = moduletype.speed
        super(Engine, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        super(Engine, self).addToFighter(fighter)
    hasAttrSpeed = True

class HyperEngine(Engine):
    def addToFighter(self, fighter): 
# does not work - disabled
#        fighter.shipHyperEngineSpeed += self.speed
        super(HyperEngine, self).addToFighter(fighter)
    pass

class PlanetEngine(Engine):
    def addToFighter(self, fighter): 
        fighter.shipPlanetEngineSpeed += self.speed
        super(PlanetEngine, self).addToFighter(fighter)
    pass

class PowerBoard(Concrete):
    shield = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.shield = moduletype.shield
        super(PowerBoard, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        fighter.shipShield += self.shield
        super(PowerBoard, self).addToFighter(fighter)
    hasAttrShield = True

class PowerUnit(Concrete):
    power = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.power = moduletype.power
        super(PowerUnit, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        fighter.shipPower += self.power
        super(PowerUnit, self).addToFighter(fighter)
    hasAttrPower = True

class Expand(Concrete):
    def resetAttrToType(self, moduletype):
        super(Expand, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        super(Expand, self).addToFighter(fighter)
    pass

class Compartment(Concrete):
    protection = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.protection = moduletype.protection
        super(Compartment, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        fighter.shipProtection += self.protection
        super(Compartment, self).addToFighter(fighter)
    hasAttrProtection = True

class Radar(Concrete):
    sensitivity = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.sensitivity = moduletype.sensitivity
        super(Radar, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        super(Radar, self).addToFighter(fighter)
    hasAttrSensitivity = True

class LongRangeRadar(Radar):
    def addToFighter(self, fighter): 
        fighter.shipLongRangeRadarSensitivity += self.sensitivity
        super(LongRangeRadar, self).addToFighter(fighter)
    pass

class NavigationRadar(Radar):
    def addToFighter(self, fighter): 
        fighter.shipNavRadarSensitivity += self.sensitivity
        super(NavigationRadar, self).addToFighter(fighter)
    pass

class BattleRadar(Radar):
    def addToFighter(self, fighter): 
        fighter.shipBattleRadarSensitivity += self.sensitivity
        super(BattleRadar, self).addToFighter(fighter)
    pass

class JammerRadar(Radar):
    def addToFighter(self, fighter): 
        fighter.shipJammerRadarSensitivity += self.sensitivity
        super(JammerRadar, self).addToFighter(fighter)
    pass

class Weapon(Concrete):
    distance = db.IntegerProperty(default=0)
    accuracy = db.IntegerProperty(default=0)
    od = db.IntegerProperty(default=0)
    rateoffire = db.IntegerProperty(default=0)
    minshot = db.IntegerProperty(default=0)
    maxshot = db.IntegerProperty(default=0)
    radius = db.IntegerProperty(default=0)
    recharge = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.distance   = moduletype.distance  
        self.accuracy   = moduletype.accuracy  
        self.od         = moduletype.od        
        self.rateoffire = moduletype.rateoffire
        self.minshot    = moduletype.minshot   
        self.maxshot    = moduletype.maxshot   
        self.radius     = moduletype.radius    
        self.recharge   = moduletype.recharge  
        super(Weapon, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        super(Weapon, self).addToFighter(fighter)
    hasAttrDistance = True
    hasAttrAccuracy = True
    hasAttrOd = True
    hasAttrRateOfFire = True
    hasAttrMinShot = True
    hasAttrMaxShot = True
    hasAttrRadius = True
    hasAttrRecharge = True

class RechargableWeapon(Weapon):
    maxcharge = db.IntegerProperty(default=0)
    charge = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.maxcharge = moduletype.maxcharge
        self.charge = moduletype.maxcharge
        super(RechargableWeapon, self).resetAttrToType(moduletype)
    def addToFighter(self, fighter): 
        # we have to add missiles weight
        fighter.shipWeight += self.charge*self.type.rockettype.weight
        super(RechargableWeapon, self).addToFighter(fighter)
    hasAttrMaxCharge = True
    hasAttrCharge = True

class RocketWeapon(RechargableWeapon):
    pass

class MineWeapon(RechargableWeapon):
    timecount = db.IntegerProperty(default=0)
    def resetAttrToType(self, moduletype):
        self.timecount = moduletype.timecount
        super(MineWeapon, self).resetAttrToType(moduletype)
    hasAttrTimeCount = True

class GravityWeapon(RechargableWeapon):
    pass

class LaserWeapon(Weapon):
    pass

class PlasmaWeapon(Weapon):
    pass

class EnergyWeapon(Weapon):
    pass

class Technology(Concrete):
    pass

class Material(Module):
    pass
  
class Rocket(Material):
    pass

